Java8 常用工具示例

本篇采用示例的形式展示Java8的常见特性应用.

线程写法

Java8之前

1
2
3
4
5
6
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("自定义线程");
}
}).start();

Java8写法

1
2
3
new Thread(()-> {
System.out.println("自定义线程");
}).start();

比较器写法

Java8之前

1
2
3
4
5
6
7
8
9
List<String> list = Arrays.asList("bb", "a", "ccc");
Collections.sort(list, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.length() - o2.length();
}
});
// [a, bb, ccc]
System.out.println(list);

Java8写法

1
2
3
Collections.sort(list, (o1, o2) -> o1.length() - o2.length());
// [a, bb, ccc]
System.out.println(list);

遍历写法

Java8之前

1
2
3
4
List<String> list = Arrays.asList("bb", "a", "ccc");
for (String li : list) {
System.out.println(li);
}

Java8写法

1
2
3
List<String> list = Arrays.asList("bb", "a", "ccc");
list.forEach(li -> System.out.println(li));
list.forEach(System.out::println);

计算过滤

1
2
3
4
5
List<Integer> numbers = Arrays.asList(20,22,1,2,1,3,3,2,4,8,16);
// 过滤集合中的偶数,去重、排序
List<Integer> filters = numbers.stream().filter(i -> i % 2 == 0).distinct().sorted().collect(Collectors.toList());
// 248162022
filters.forEach(System.out::print);

对列表每个元素应用函数

1
2
3
4
5
List<String> list = Arrays.asList("USA", "Japan", "France", "Germany", "Italy","Canada");
// 将字符串换成大写并用点号链接起来
String str = list.stream().map(x -> x.toUpperCase()).collect(Collectors.joining("、"));
// USA、JAPAN、FRANCE、GERMANY、ITALY、CANADA
System.out.println(str);

lambda表达式中的Map Reduce

1
2
3
4
List<Integer> costBeforeTax = Arrays.asList(100, 200, 300, 400);
double bill = costBeforeTax.stream().map((cost) -> cost + .10*cost).reduce((sum, cost) -> sum + cost).get();
// 1100.0
System.out.println("集合中的元素每个增大10%后的结果总和: " + bill);

集合元素计算

1
2
3
4
5
6
List<Integer> primes = Arrays.asList(2, 3, 5, 7, 11, 13, 17, 19, 23, 29);
IntSummaryStatistics stats = primes.stream().mapToInt((x) -> x).summaryStatistics();
System.out.println("集合中最大值 : " + stats.getMax());
System.out.println("集合中最小值 : " + stats.getMin());
System.out.println("集合元素总数 : " + stats.getSum());
System.out.println("集合元素均值 : " + stats.getAverage());

集合元素分组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public static void main(String[] args) {
List<Person> list = Arrays.asList(new Person(10, "Elon"), new Person(12, "Dennisit"), new Person(10, "Alone"));
// [{"age":10,"name":"Elon"}, {"age":12,"name":"Dennisit"}, {"age":10,"name":"Alone"}]
System.out.println(list);
Map<Integer, List<Person>> groupByAge = list.stream().collect(Collectors.groupingBy(Person::getAge, Collectors.toList()));
// {10=[{"age":10,"name":"Elon"}, {"age":10,"name":"Alone"}], 12=[{"age":12,"name":"Dennisit"}]}
System.out.println(groupByAge);
List<String> names = list.stream().map(e->e.getName()).collect(Collectors.toList());
// [Elon, Dennisit, Alone]
System.out.println(names);
List<Integer> ages = list.stream().map(e->e.getAge()).distinct().collect(Collectors.toList());
// [10, 12]
System.out.println(ages);
}

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
static class Person{
private int age;
private String name;

public String toString(){
return JSON.toJSONString(this);
}
}

集合对象排序

1
2
3
4
5
6
7
8
9
public static void main(String[] args) {
List<Person> list = Arrays.asList(new Person(10, "Elon"), new Person(12, "Dennisit"), new Person(10, "Alone"));
// 按照年龄降序
List<Person> sort = list.stream()
.sorted(Comparator.comparing(Person::getAge).reversed())
.collect(Collectors.toList());
// [{"age":12,"name":"Dennisit"}, {"age":10,"name":"Elon"}, {"age":10,"name":"Alone"}]
System.out.println(sort);
}

函数式接口

1
2
3
4
5
6
7
// 函数接口定义
Predicate<String> startWithJ = (n) -> n.startsWith("J");
Predicate<String> containedA = (n) -> n.contains("a");

List<String> languages = Arrays.asList("Java", "Scala", "C++", "Haskell", "Lisp");
// 使用逻辑函数合并Predicate 执行结果: Java,Scala,Haskell,
languages.stream().filter(startWithJ.or(containedA)).forEach(li -> System.out.print(li + ","));

函数接口应用

我们在用全文索引的时候需要将数据先存储到索引然后进行搜索,索引数据一般操作有全量索引、增量索引、实施索引, 索引创建方式可能有databus、基于消息机制、或者基于定时任务等等.

通常我们把DB数据转换为搜索引擎的索引的时候,分两步:

  • 筛选出待索引的数据
  • 将目标数据进行索引

针对这个行为我们来定义行为规约, 函数接口定义时使用@FunctionalInterface

筛选数据行为
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 筛选数据行为规约
*/
@FunctionalInterface
public interface DataCriteria<T> {

/**
* 分页加载数据
* @param page 页码
* @param size 页量
* @return 加载的数据集合
*/
public List<T> loader(int page, int size);

}
索引数据行为规约
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 索引数据行为规约
*/
@FunctionalInterface
public interface IndexCriteria {

/**
* 数据索引行为
* @param list 待索引的数据
* @return
*/
public void index(List<?> list);

}

全量索引行为抽象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63

/**
* 每次批量处理的数据一次最大500个
*/
public static final Integer DEFAULT_BATCH_SIZE = 500;

/**
* 私有化构造
*/
private IndexAction(){

}


/**
* 执行全量索引
* @param dataCriteria 数据加载行为
* @param indexCriteria 索引数据行为
*/
public static void fullyIndex(DataCriteria dataCriteria, IndexCriteria indexCriteria) {
fullyIndex(DEFAULT_BATCH_SIZE, dataCriteria, indexCriteria);
}


/**
* 执行全量索引
* @param size 每个批次索引的数据大小
* @param dataCriteria 数据加载行为
* @param indexCriteria 索引数据行为
*/
public static void fullyIndex(int size, DataCriteria dataCriteria, IndexCriteria indexCriteria) {
// 游标页从第一页开始
int current = 1;
// 约束批量处理的大小
size = NumberUtils.restrainNum(size, 0, DEFAULT_BATCH_SIZE);
while(true){
int cursor = cursor(current, size, dataCriteria, indexCriteria);
if (cursor == current) {
LOG.info("[创建索引] 创建完成, {}*{}数据", size, cursor);
break;
}
current = cursor;
}
}

/**
* 游标扫描处理
* @param page 游标页
* @param size 每个批次索引的数据大小
* @param dataCriteria 数据加载行为
* @param indexCriteria 索引数据行为
* @return
*/
private static int cursor(int page, int size, DataCriteria dataCriteria, IndexCriteria indexCriteria){
Assert.notNull(dataCriteria, "数据操作不能为空");
Assert.notNull(indexCriteria, "操作操作不能为空");
List list = dataCriteria.loader(page, size);
if(CollectionUtils.isNotEmpty(list)){
indexCriteria.index(list);
++ page;
}
return page;
}

上面我们做了简单的全量索引行为抽象, 从第一页开始分页加载数据进行索引,直到我们加载到的数据为空,本次索引行为结束

特定索引行为抽象

1
2
3
4
5
6
7
8
9
10
11
12
/**
* 指定数据索引
* @param list 待索引数据
* @param indexCriteria 索引行为
*/
public static void pointIndex(List<?> list, IndexCriteria indexCriteria){
if(CollectionUtils.isEmpty(list)){
return;
}
Assert.notNull(indexCriteria, "操作操作不能为空");
indexCriteria.index(list);
}

该抽象比较简单,直接进行指定的数据索引即可.

规约使用

上面我们定义了规约, 接下来直接展示如何优雅的基于规约装载数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
* 全量索引
*/
@Override
public void fullyIndex() {
IndexAction.fullyIndex(
// 分页加载数据
(p, s) -> {
return merchantService.selectList(p, s);
},
// 数据进行索引
(x)-> merchantEsRepository.indexList((List<MerchantIndex>)x)
);
}

/**
* 特定索引
* @param ids 主键编号集合
* @throws Exception
*/
@Override
public void pointIndex(List<Integer> ids) throws Exception{
List<MerchantIndex> list = merchantService.selectList(ids);
IndexAction.pointIndex(list, (x) -> merchantEsRepository.indexList((List<MerchantIndex>) x));
}

示例中分别展示了全量索引和特定数据索引的规约应用,通过函数接口功能打包.我们把全量和增量的行为就隐藏在了规约行为中,使业务代码更简洁优雅.

接口方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public static void main(String[] args) {
MathOperation addition = (int a, int b) -> a + b;
// 默认方法
addition.print();
// 9
System.out.println(addition.operation(5, 4));
}

interface MathOperation {

// 接口方法
int operation(int a, int b);

default void print(){
System.out.println("默认方法");
}
}

Java8日期操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/创建日期
LocalDate date = LocalDate.of(2017,1,21); //2017-01-21
int year = date.getYear() //2017
Month month = date.getMonth(); //JANUARY
int day = date.getDayOfMonth(); //21
DayOfWeek dow = date.getDayOfWeek(); //SATURDAY
int len = date.lengthOfMonth(); //31(days in January)
boolean leap = date.isLeapYear(); //false(not a leap year)

//时间的解析和格式化
LocalDate date = LocalDate.parse("2017-01-21");
LocalTime time = LocalTime.parse("13:45:20");

LocalDateTime now = LocalDateTime.now();
now.format(DateTimeFormatter.BASIC_ISO_DATE);

//合并日期和时间
LocalDateTime dt1 = LocalDateTime.of(2017, Month.JANUARY, 21, 18, 7);
LocalDateTime dt2 = LocalDateTime.of(localDate, time);
LocalDateTime dt3 = localDate.atTime(13,45,20);
LocalDateTime dt4 = localDate.atTime(time);
LocalDateTime dt5 = time.atDate(localDate);

//操作日期
LocalDate date1 = LocalDate.of(2014,3,18); //2014-3-18
LocalDate date2 = date1.plusWeeks(1); //2014-3-25
LocalDate date3 = date2.minusYears(3); //2011-3-25
LocalDate date4 = date3.plus(6, ChronoUnit.MONTHS); //2011-09-25

日期格式化

java.util.date和java.time.LocalDateTime格式化

应用示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
* 格式化日期
* @param date 待格式化的日期
* @param pattern 格式化正则
* @return 格式化结果串
*/
public static String format(Date date, String pattern){
return new SimpleDateFormat(pattern).format(date);
}

/**
* 格式化日期
* @param localDateTime 待格式化的日期
* @param pattern 格式化正式
* @return 格式化结果串
*/
public static String format(LocalDateTime localDateTime, String pattern){
return localDateTime.format(DateTimeFormatter.ofPattern(pattern));
}

/**
* 格式化日期
* @param localDate 待格式化的日期
* @param pattern 格式化正则, 这里使用的类型 {@link LocalDate}, 所以正则只能设定到天
* @return 格式化结果串
*/
public static String format(LocalDate localDate, String pattern){
return localDate.format(DateTimeFormatter.ofPattern(pattern));
}
示例测试
1
2
3
4
5
6
// 2017-08-28 15:45:02
System.out.println(format(new Date(), "yyyy-MM-dd HH:mm:ss"));
// 2017-08-28 15:45:02
System.out.println(format((LocalDateTime.now()), "yyyy-MM-dd HH:mm:ss"));
// 2017-08-28
System.out.println(format((LocalDateTime.now().toLocalDate()), "yyyy-MM-dd"));

日期转换

java.util.date和java.time.LocalDateTime互相转换

应用示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* 将 {@link LocalDateTime} 转换成 {@link Date}
* @param localDateTime {@link LocalDateTime} 待转换的日期
* @return 转换成Date结果
*/
public static Date from(LocalDateTime localDateTime){
Instant instant = localDateTime.atZone(ZoneId.systemDefault()).toInstant();
return Date.from(instant);
}

/**
* 将 {@link Date} 转换成 {@link LocalDateTime}
* @param date {@link Date} 待转换的日期
* @return 转换成 {@link LocalDateTime} 结果
*/
public static LocalDateTime from(Date date){
return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
}
示例测试
1
2
3
4
5
6
String patternTime = "yyyy-MM-dd HH:mm:ss";
Date now = new Date();
// 2017-08-28 14:47:10
System.out.println(format(from(now), patternTime));
// 2017-08-28 14:47:10
System.out.println(format(from(LocalDateTime.now()), patternTime));

日期区间集合

计算两端日期之间内的日期天数集合

示例代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
/**
* 获取{@link Date}在开始时间和结束时间内的日期时间段{@link Date}集合
* @param start 开始时间
* @param end 结束时间
* @return 时间天数集合
*/
public static List<Date> dateZones(Date start, Date end){
return dateZones(from(start), from(end));
}


/**
* 获取 {@link LocalDate} 在开始时间和结束时间内的日期时间段 {@link LocalDate} 集合
* @param start 开始时间
* @param end 结束时间
* @return 时间集合
*/
public static List<Date> dateZones(LocalDate start, LocalDate end){
return Stream.iterate(start, x -> x.plusDays(1))
.limit(ChronoUnit.DAYS.between(start, end) + 1)
.map(e -> Date.from(e.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant()))
.collect(Collectors.toList());
}


/**
* 获取{@link LocalDateTime} 在开始时间和结束时间内的日期时间段{@link Date}集合
* @param start 开始时间
* @param end 结束时间
* @return 时间天数集合
*/
public static List<Date> dateZones(LocalDateTime start, LocalDateTime end){
// 用起始时间作为流的源头,按照每次加一天的方式创建一个无限流
return Stream.iterate(start.toLocalDate(), x -> x.plusDays(1))
// 截断无限流,长度为起始时间和结束时间的差+1个
.limit(ChronoUnit.DAYS.between(start, end) + 1)
// 由于最后要的是字符串,所以map转换一下
.map(e -> Date.from(e.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant()))
// 把流收集为List
.collect(Collectors.toList());
}


/**
* 获取{@link Date}在开始时间和结束时间内的日期时间段{@link LocalDate}集合
* @param start 开始时间
* @param end 结束时间
* @return 时间集合
*/
public static List<LocalDate> localDateZones(Date start, Date end){
return localDateZones(from(start), from(end));
}


/**
* 获取 {@link LocalDate} 在开始时间和结束时间内的日期时间段 {@link LocalDate} 集合
* @param start 开始时间
* @param end 结束时间
* @return 时间集合
*/
public static List<LocalDate> localDateZones(LocalDate start, LocalDate end){
return Stream.iterate(start, x -> x.plusDays(1))
.limit(ChronoUnit.DAYS.between(start, end) + 1)
.collect(Collectors.toList());
}

/**
* 获取 {@link LocalDateTime} 在开始时间和结束时间内的日期时间段 {@link LocalDate} 集合
* @param start 开始时间
* @param end 结束时间
* @return 时间集合
*/
public static List<LocalDate> localDateZones(LocalDateTime start, LocalDateTime end){
// 用起始时间作为流的源头,按照每次加一天的方式创建一个无限流
return Stream.iterate(start.toLocalDate(), x -> x.plusDays(1))
// 截断无限流,长度为起始时间和结束时间的差+1个
.limit(ChronoUnit.DAYS.between(start, end) + 1)
.map(e -> e.atStartOfDay().toLocalDate())
// 把流收集为List
.collect(Collectors.toList());
}
示例测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
String patternDate = "yyyy-MM-dd";
List<Date> dateList = Arrays.asList(new Date(2017-1900, 11, 30), new Date(2018-1900, 0, 3));

// 2017-12-30
System.out.println("开始时间:" + format(dateList.get(0), patternDate) + ", 结束时间:" + format(dateList.get(1), patternDate));

// [2017-12-30, 2017-12-31, 2018-01-01, 2018-01-02, 2018-01-03]
System.out.println(dateZones(dateList.get(0), dateList.get(1)).stream().map(x -> format(x, patternDate)).collect(Collectors.toList()));
// [2017-12-30, 2017-12-31, 2018-01-01, 2018-01-02, 2018-01-03]
System.out.println(localDateZones(dateList.get(0), dateList.get(1)).stream().map(x -> format(x, patternDate)).collect(Collectors.toList()));


LocalDateTime now = LocalDateTime.of(2017, Month.DECEMBER, 30, 0, 0, 0);

// 2017-12-30
System.out.println(format(now, patternDate));

// [2017-12-30, 2017-12-31, 2018-01-01, 2018-01-02, 2018-01-03]
System.out.println(dateZones(now, now.plus(4, ChronoUnit.DAYS))
.stream().map(x -> format(x, patternDate)).collect(Collectors.toList()));

// [2017-12-30, 2017-12-31, 2018-01-01, 2018-01-02, 2018-01-03]
System.out.println(localDateZones(now, now.plus(4, ChronoUnit.DAYS))
.stream().map(x -> format(x, patternDate)).collect(Collectors.toList()));

// [2017-12-30, 2017-12-31, 2018-01-01, 2018-01-02, 2018-01-03]
System.out.println(localDateZones(now.toLocalDate(), now.toLocalDate().plus(4, ChronoUnit.DAYS))
.stream().map(x -> format(x, patternDate)).collect(Collectors.toList()));

日期加减

示例代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
String patternDate = "yyyy-MM-dd HH:mm:ss";

LocalDateTime now = LocalDateTime.of(2017, Month.DECEMBER, 30, 0, 0, 0);

// 当前时间: 2017-12-30 00:00:00
System.out.println("当前时间: " + format(now, patternDate));

// 30秒前: 2017-12-29 23:59:30
System.out.println("30秒前: " + format(now.plus(-30, ChronoUnit.SECONDS), patternDate));

// 5分钟后: 2017-12-30 00:05:00
System.out.println("5分钟后: " + format(now.plus(5, ChronoUnit.MINUTES), patternDate));

// 2天前: 2017-12-28 00:00:00
System.out.println("2天前: " + format(now.plus(-2, ChronoUnit.DAYS), patternDate));

// 2天后: 2018-01-01 00:00:00
System.out.println("2天后: " + format(now.plus(2, ChronoUnit.DAYS), patternDate));

// 1周后: 2018-01-06 00:00:00
System.out.println("1周后: " + format(now.plusWeeks(1), patternDate));

// 1月前: 2017-11-30 00:00:00
System.out.println("1月前: " + format(now.plus(-1, ChronoUnit.MONTHS), patternDate));

// 1月后: 2018-01-30 00:00:00
System.out.println("1月后: " + format(now.plus(1, ChronoUnit.MONTHS), patternDate));

// 1年后: 2018-12-30 00:00:00
System.out.println("1年后: " + format(now.plus(1, ChronoUnit.YEARS), patternDate));

日期推算

示例代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
String patternDate = "yyyy-MM-dd";

LocalDateTime now = LocalDateTime.of(2017, Month.DECEMBER, 30, 0, 0, 0);

// 当前时间: 2017-12-30
System.out.println("当前时间: " + format(now, patternDate) + " ,是否闰年: " + now.toLocalDate().isLeapYear());

// 当前月份: 十二月
System.out.println("当前月份: " + Month.from(now).getDisplayName(TextStyle.FULL, Locale.CHINA));

// 当前星期: 星期六
System.out.println("当前星期: " + DayOfWeek.from(now).getDisplayName(TextStyle.FULL, Locale.CHINA));

// 需要注意:java8提供的获取的本周第一天和本周最后一天是西方的界定方式, 第一天是周末, 最后一天是周六, 和中国的不太一样
// 本周初第一天:2017-12-24
System.out.println("本周初第一天: " + format(now.with(WeekFields.of(Locale.CHINA).dayOfWeek(),1L), patternDate));

// 本周最后一天:2017-12-30
System.out.println("本周最后一天: " + format(now.with(WeekFields.of(Locale.CHINA).dayOfWeek(),7L), patternDate));

// 本月初第一天:2017-12-01
System.out.println("本月初第一天: " + format(now.with(TemporalAdjusters.firstDayOfMonth()), patternDate));

// 本月最后一天:2017-12-31
System.out.println("本月最后一天: " + format(now.with(TemporalAdjusters.lastDayOfMonth()), patternDate));

// 本年最后一天:2017-01-01
System.out.println("本年最后一天: " + format(now.with(TemporalAdjusters.firstDayOfYear()), patternDate));

// 本年最后一天:2017-12-31
System.out.println("本年最后一天: " + format(now.with(TemporalAdjusters.lastDayOfYear()), patternDate));

Optional类说明

Optional类的Javadoc描述如下:
这是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。

Optional基础对象处理

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 有则返回, 无则由函数产生
System.out.println(Optional.ofNullable(null).orElseGet(() -> Arrays.asList(1,2,3)));

// 元素存在输出true,反之输出false
System.out.println(Optional.ofNullable("Elon").isPresent());

// 不为空时输出元素,反之输出“默认值”
System.out.println(Optional.ofNullable("Elon").orElse("默认值"));

// 元素存在输出元素,反之抛出异常
System.out.println(Optional.ofNullable("Elon").orElseThrow(IllegalArgumentException::new));


// 元素存在输出true,反之输出false
System.out.println(Optional.ofNullable(null).isPresent());

// 不为空时输出元素,反之输出“默认值”
System.out.println(Optional.ofNullable(null).orElse("默认值"));

// 元素存在输出元素,反之抛出异常
System.out.println(Optional.ofNullable(null).orElseThrow(IllegalArgumentException::new));

示例输出

1
2
3
4
5
6
7
8
9
10
[1, 2, 3]
true
Elon
Elon
false
默认值

java.lang.IllegalArgumentException
at java.util.Optional.orElseThrow(Optional.java:290)
...

Optional基础集合处理

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
List<Integer> list = Arrays.asList(1, 2, 3, 2, 5, 3);

// 集合不为空的时候进行遍历去重,反之输出空集合
System.out.println(Optional.ofNullable(list).orElse(Lists.newArrayList()).stream().distinct().collect(Collectors.toList()));

// 集合不为空的时候进行遍历去重,反之抛出异常
System.out.println(Optional.ofNullable(list).orElseThrow(IllegalArgumentException::new).stream().distinct().collect(Collectors.toList()));

list = null;

// 集合不为空的时候进行遍历去重,反之输出空集合
System.out.println(Optional.ofNullable(list).orElse(Lists.newArrayList()).stream().distinct().collect(Collectors.toList()));

// 集合不为空的时候进行遍历去重,反之抛出异常 IllegalArgumentException
System.out.println(Optional.ofNullable(list).orElseThrow(IllegalArgumentException::new).stream().distinct().collect(Collectors.toList()));

示例输出

1
2
3
4
5
6
7
[1, 2, 3, 5]
[1, 2, 3, 5]
[]

java.lang.IllegalArgumentException
at java.util.Optional.orElseThrow(Optional.java:290)
...

Optional复杂集合处理

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
class Person{

private String name;
private int age;

public String toString(){
return JSON.toJSONString(this);
}
}

@Test
public void list(){
Person person = null;
System.out.println(Optional.ofNullable(person).map(x -> x.getName()).orElse("默认姓名"));
System.out.println(Optional.ofNullable(person).map(x -> x.getAge()).orElse(18));

List<Person> list = Arrays.asList(new Person("p1", 10), new Person("p2", 15));

System.out.println("最大年龄用户:" + Optional.ofNullable(list).orElseThrow(NullPointerException:: new).stream().collect(Collectors.maxBy(Comparator.comparingInt(Person::getAge))).get());
System.out.println("最大年龄数值:" + Optional.ofNullable(list).orElseThrow(NullPointerException:: new).stream().collect(Collectors.maxBy(Comparator.comparingInt(Person::getAge))).get().getAge());
System.out.println("年龄均值数值:" + Optional.ofNullable(list).orElseThrow(NullPointerException:: new).stream().collect(Collectors.averagingInt(Person::getAge)));
}

示例输出

1
2
3
4
5
默认姓名
18
最大年龄用户:{"age":15,"name":"p2"}
最大年龄数值:15
年龄均值数值:12.5

Nashorn JavaScript引擎

1
2
3
4
5
6
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("JavaScript");
// jdk.nashorn.api.scripting.NashornScriptEngine
System.out.println( engine.getClass().getName() );
// Result:2.0
System.out.println("Result:" + engine.eval("function f() { return 1; }; f() + 1;"));

Base64支持

1
2
3
4
5
final String text = "Base64 finally in Java 8!";
final String encoded = Base64.getEncoder().encodeToString(text.getBytes(StandardCharsets.UTF_8));
System.out.println(encoded);
final String decoded = new String(Base64.getDecoder().decode(encoded), StandardCharsets.UTF_8);
System.out.println(decoded);

Java8新特性示例图